package DEEPERsource.DEEPERsource.source.jung.entities;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import edu.uci.ics.jung.exceptions.FatalException;
import edu.uci.ics.jung.graph.DirectedEdge;
import edu.uci.ics.jung.graph.Edge;
import edu.uci.ics.jung.graph.Vertex;
import edu.uci.ics.jung.graph.impl.AbstractSparseVertex;
import graph.Keys;

/**
 * Vertex implementation for directed graph with possible parallel arcs.
 * Intended to use with DirectedSparseEdge in DependencyGraph
 * @author tfayruzo
 *
 */

public class DependencyVertex extends AbstractSparseVertex {

	private List<Vertex> pred;

	private List<DirectedEdge> predEdges;

	private List<Vertex> succ;

	private List<DirectedEdge> succEdges;
	
	@SuppressWarnings("unchecked")
	public boolean isSame(DependencyVertex v){
		for(Iterator<Object> i = getUserDatumKeyIterator(); i.hasNext();){
			Object key = i.next();
			Object value = getUserDatum(key);
			Object anotherValue = v.getUserDatum(key);
			if(!value.equals(anotherValue)){
				return false;
			}
		}
		return true;
	}
	
	public DirectedEdge findEdge(int index){
		for(DirectedEdge pred : predEdges){
			Integer i = (Integer)pred.getUserDatum(Keys.INDEX);
			if(i != null && i==index){
				return pred;
			}
		}
		for(DirectedEdge succ : succEdges){
			Integer i = (Integer)succ.getUserDatum(Keys.INDEX);
			if(i != null && i==index){
				return succ;
			}
		}
		return null;
	}
	
	@Override
	protected void initialize() {
		pred = new ArrayList<Vertex>();
		succ = new ArrayList<Vertex>();
		predEdges = new ArrayList<DirectedEdge>();
		succEdges = new ArrayList<DirectedEdge>();
		super.initialize();
	}

	@Override
	protected void addNeighbor_internal(Edge e, Vertex v) {
		if (!(e instanceof DirectedEdge))
			throw new IllegalArgumentException("This vertex "
					+ "implementation only accepts directed edges");
		DirectedEdge edge = (DirectedEdge) e;
		if (!(edge.getSource() instanceof DependencyVertex)
				|| !(edge.getDest() instanceof DependencyVertex))
			throw new IllegalArgumentException("Edge "
					+ "sould connect two DependencyVertex objects");
		boolean added = false;
		if (this == edge.getSource()) {
			succ.add(edge.getDest());
			succEdges.add(edge);
			added = true;
		}
		if (this == edge.getDest()) {
			pred.add(edge.getDest());
			predEdges.add(edge);
			added = true;
		}
		if (!added)
			throw new IllegalArgumentException("Internal error: "
					+ "this vertex is not incident to " + e);
	}
	
	@Override
	protected void removeNeighbor_internal(Edge e, Vertex v) {
		String error = "Internal error: " + "edge " + e
				+ " not incident to vertex ";
		if (succ.contains(v) && v.isDest(e)) {
			//e is an outgoing edge of this vertex -> v is a successor
			//since vertices could repeat, we locate the index of element 
			//to remove
			int indexToRemove = succEdges.indexOf(e);
			if (succ.remove(indexToRemove) == null || succEdges.remove(indexToRemove) == null)
				throw new FatalException(error + v);
		} else if (pred.contains(v) && v.isSource(e)) { 
			// e is an	incoming edge of this vertex -> v is a predecessor
			//since vertices could repeat, we locate the index of element 
			//to remove
			int indexToRemove = predEdges.indexOf(e);
			if (pred.remove(indexToRemove) == null || predEdges.remove(indexToRemove) == null)
				throw new FatalException(error + v);
		} else
			throw new FatalException(error + this);
	}

	@Override
	protected Collection getEdges_internal() {
		List<DirectedEdge> result = new ArrayList<DirectedEdge>();
		result.addAll(predEdges);
		result.addAll(succEdges);
		return result;
	}

	@Override
	protected Collection getNeighbors_internal() {
		List<Vertex> result = new ArrayList<Vertex>();
		result.addAll(pred);
		result.addAll(succ);
		return result;	
	}

	public Set getInEdges() {
		Set<DirectedEdge> edges = new HashSet<DirectedEdge>();
		edges.addAll(predEdges);
		return edges;
	}

	public Set getOutEdges() {
		Set<DirectedEdge> edges = new HashSet<DirectedEdge>();
		edges.addAll(succEdges);
		return edges;
	}

	public Set getPredecessors() {
		Set<Vertex> vertices = new HashSet<Vertex>();
		vertices.addAll(pred);
		return vertices;	
	}

	public Set getSuccessors() {
		Set<Vertex> vertices = new HashSet<Vertex>();
		vertices.addAll(succ);
		return vertices;
	}

	public int inDegree() {
		return predEdges.size();
	}

	public boolean isDest(Edge e) {
		if (e.getGraph() == this.getGraph())
            return equals(((DirectedEdge)e).getDest());
        else
            return false;
	}

	public boolean isPredecessorOf(Vertex v) {
		return succ.contains(v);
	}

	public boolean isSource(Edge e) {
		if (e.getGraph() == this.getGraph())
            return equals(((DirectedEdge)e).getSource());
        else
            return false;
	}

	public boolean isSuccessorOf(Vertex v) {
		return pred.contains(v);
	}

	public int numPredecessors() {
		return pred.size();
	}

	public int numSuccessors() {
		return succ.size();
	}

	public int outDegree() {
		return succEdges.size();
	}

}
